package com.hyc.DataStructure.LinkedList;

/**
 * @projectName: DataStructure
 * @package: com.hyc.DataStructure.LinkedList
 * @className: CircleSingleLinkeListDemo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2021/12/21 15:42
 * @version: 1.0
 */
public class CircleSingleLinkeListDemo {
    public static void main(String[] args) {
        CircleSingleLinkeList list = new CircleSingleLinkeList();
        list.CreateListNode(5);
        list.showListBoy();
        list.JosephuFunc(5, 1, 2);
    }
}


// 环形单向链表
class CircleSingleLinkeList {
    Boy first = null;

    /**
     * @author 冷环渊 Doomwatcher
     * @context: 一个环形链表  参数是这个链表有多少节点
     * @date: 2021/12/21 15:47
     * @param nums
     * @return: void
     */
    public void CreateListNode(int nums) {

        if (nums < 1) {
            System.out.println("无法开始游戏，因为链表小于最小的游戏节点数量");
            return;
        }
        // 创建一个 辅助之间用于遍历和指向节点
        Boy curBoy = null;
        for (int i = 1; i <= nums; i++) {
            Boy boy = new Boy(i);
            //如果只有一个节点的话 也可以生成环形链表
            if (i == 1) {
                first = boy;
                first.setNext(first);
                curBoy = first;//将辅助指针指向链表的第一个节点 形成闭环
            } else {
                //    如果不是一个的话就生成多个节点
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }

    public void showListBoy() {
        if (first == null) {
            System.out.println("链表为空，不能遍历");
            return;
        }
        //   frist 节点不能移动 我们创建一个指针
        Boy curboy = first;
        while (true) {
            System.out.printf("小孩的编号是： %d \n", curboy.getNo());
            // 说明已经遍历完毕
            if (curboy.getNext() == first) {
                break;
            }
            //后移 curboy
            curboy = curboy.getNext();
        }

    }

    /**
     * @author 冷环渊 Doomwatcher
     * @context: 这里我们以小孩做游戏来解决约瑟夫问题，
     * @date: 2021/12/22 21:16
     * @param nums 一共有多少个小孩
     * @param start 从那个小孩开始报数
     * @param index 每隔几个小孩报数一次
     * @return: void
     */
    public void JosephuFunc(int nums, int start, int index) {
        if (first == null || start < 1 || start > nums) {
            System.out.println("参数不对，请重新输入参数");
            return;
        }
        Boy helper = first;
        //将 helper遍历到最后一个节点
        while (true) {
            if (helper.getNext() == first) {
                break;
            }
            helper = helper.getNext();
        }
        // 小孩子 报数之前，要遍历指针到从start开始的小孩的节点
        for (int i = 0; i < start - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数的时候 让 first和helper 同时移动m-1次 然后出圈
        //这里循环操作 知道圈中的一个节点
        while (true) {
            //说明列表只有一个节点
            if (helper == first) {
                break;
            }
            //循环之后将 指针后移
            for (int i = 0; i < index - 1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //    这个时候 first选中的节点就是要出圈的小孩
            System.out.printf("小孩 %d 出圈\n", first.getNo());
            //出圈操作
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后剩下的小孩 %d \n", first.getNo());
    }
}

//环形链表 约瑟夫问题 节点
class Boy {
    private int No;
    private Boy Next;

    public Boy(int no) {
        this.No = no;
    }

    public int getNo() {
        return No;
    }

    public void setNo(int no) {
        No = no;
    }

    public Boy getNext() {
        return Next;
    }

    public void setNext(Boy next) {
        Next = next;
    }

}